﻿@page "/GridMasterDetail"

@using BlazorDemo.Data.SalesViewer
@using Sale = BlazorDemo.Data.SalesViewer.Sale
@using Product = BlazorDemo.Data.SalesViewer.Product
@using System.Collections.Concurrent
@using System.Threading
@implements IDisposable
@inject ISalesViewerDataProvider SalesViewerDataProvider
@layout DataProviderAccessArea<ISalesViewerDataProvider>

<div class="demo-description mw-1100">
    <h2>Data Grid - Master-Detail View</h2>
    <p>
        The <a class="helplink" target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1">Data Grid</a> component allows you to build master-detail layouts of any complexity and depth with a detail row template. This template defines the way detail data is presented.
    </p>
    <p>
        This demo module illustrates how to use a nested grid component to visualize a master-detail relationship between two data tables. To do this, follow the steps below.
    </p>
    <ol>
        <li>Add two <a class="helplink" target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1">Data Grid</a> components to your application.</li>
        <li>Bind the first grid (master) to a master data source and enable its <a class="helplink" target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1.ShowDetailRow">ShowDetailRow</a> property.</li>
        <li>Bind the second grid (detail) to a detail data source that uses the template's context object as a filter criterion.</li>
        <li>(Optional) To automatically collapse an expanded detail row when a new detail row is displayed, set the <a class="helplink" target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1.AutoCollapseDetailRow">AutoCollapseDetailRow</a> property to <b>true</b>.</li>
    </ol>
    <p>
        Create a <a class="helplink" target="_blank" href="https://docs.devexpress.com/Blazor/DevExpress.Blazor.DxDataGrid-1.DetailRowTemplate">DetailRowTemplate</a> template for the master grid and add the detail grid to this template.
    </p>
</div>


<DxDataGrid @ref="@grid"
            DataAsync="@SalesViewerDataProvider.GetProducts"
            KeyFieldName="Id"
            AutoCollapseDetailRow="true"
            ShowDetailRow="true"
            SelectionMode="DataGridSelectionMode.None"
            PageSize="4"
            CssClass="mw-1100">
    <Columns>
        <DxDataGridColumn Field="@nameof(Product.Name)" />
        <DxDataGridColumn Field="@nameof(Product.Description)" />
        <DxDataGridSpinEditColumn Field="@nameof(Product.ListPrice)" DisplayFormat="c" />
        <DxDataGridSpinEditColumn Field="@nameof(Product.UnitsInInventory)" />
        <DxDataGridSpinEditColumn Field="@nameof(Product.UnitsInManufacturing)" />
    </Columns>
    <DetailRowTemplate Context="dataItem">
        <DxTabs>
            <DxTabPage Text="Sales">
                <div class="p-3">
                    <DxDataGrid DataAsync="@(async (cancellationToken) => {
                            var allSales = await SalesViewerDataProvider.GetSales(cancellationToken);
                            return allSales.Where(x => x.ProductId == dataItem.Id);
                            })"
                                SelectionMode="DataGridSelectionMode.None"

                                LayoutRestoring="@GetDetailsGridLayoutRestoringDelegate(dataItem)"
                                LayoutChanged="@GetDetailsGridLayoutChangedDelegate(dataItem)">
                        <DxDataGridDateEditColumn Field="@nameof(Sale.SaleDate)" />
                        <DxDataGridSpinEditColumn Field="@nameof(Sale.Units)" />
                        <DxDataGridSpinEditColumn Field="@nameof(Sale.TotalCost)" DisplayFormat="c" />
                        <DxDataGridSpinEditColumn Field="@nameof(Sale.Discount)" DisplayFormat="c" />
                    </DxDataGrid>
                </div>
            </DxTabPage>
            <DxTabPage Text="Plant Info">
                @{
                    var plantInfo = PlantsDataSource?.FirstOrDefault(x => x.Id == dataItem.PlantId);
                }
                <div class="p-3">
                    <b>Name:</b>
                    <p>
                        @plantInfo.Name
                    </p>
                    <b>Address:</b>
                    <p>
                        @plantInfo.Address
                    </p>
                    <b>Zip:</b>
                    <p>
                        @plantInfo.Zip
                    </p>
                </div>
            </DxTabPage>
        </DxTabs>
    </DetailRowTemplate>
</DxDataGrid>

<CodeSnippet_Grid_MasterDetail></CodeSnippet_Grid_MasterDetail>


@code {

    IEnumerable<Plant> PlantsDataSource;

    DxDataGrid<Product> grid;

    readonly ConcurrentDictionary<int, string> detailGridsLayoutCache = new ConcurrentDictionary<int, string>();
    protected override async Task OnInitializedAsync()
    {
        PlantsDataSource = await SalesViewerDataProvider.GetPlants();
    }

    protected Func<CancellationToken, Task<IEnumerable<Sale>>> GetSalesDataByProductAsync(Product product)
    {
        return GetProductSalesAsync;

        async Task<IEnumerable<Sale>> GetProductSalesAsync(CancellationToken cancellationToken)
        {
            var allSales = await SalesViewerDataProvider.GetSales(cancellationToken);
            return allSales.Where(x => x.ProductId == product.Id);
        }
    }
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var products = await @SalesViewerDataProvider.GetProducts();
            var firstProduct = products.First();
            grid.DetailRows.ExpandRow(firstProduct.Id);
        }
    }
    protected Action<IDataGridLayout> GetDetailsGridLayoutRestoringDelegate(Product product) {
        return (layout) => {
            if (detailGridsLayoutCache.TryGetValue(product.Id, out string previousLayoutJson))
                layout.LoadLayout(previousLayoutJson);
        };
    }

    protected Action<IDataGridLayout> GetDetailsGridLayoutChangedDelegate(Product product) {
        return (layout) => {
            var layoutJson = layout.SaveLayout();
            detailGridsLayoutCache.AddOrUpdate(product.Id, layoutJson, (id, layout) => {
                return layoutJson;
            });
        };
    }
    void IDisposable.Dispose() {
        detailGridsLayoutCache.Clear();
    }
}
